// add by zl 20240914
// WriteFile 是一个写文件的封装
// ReadFile 是一个读文件的封装
// 由于写代码时候，经常会调试，有时候也经常写文件，读文件，每次读写文件都需要打开文件，太繁琐，为了方便读写文件，所以设计这两个类

// WriteFile  ReadFile 这个两个类都是支持线程安全的，
// 例如：WriteFile 如果两个线程，一个线程正在写文件 W2File，另一个线程突然关闭这个文件 CloseFile，也是可以安全退出，
// 退出时候会，先从map 表中删除，拿到一个共享指针，看一下引用计数,如果引用计数大于1，说明正在写文件，等待引用计数为1， 安全退出。
// 程序只有在 GetFilePtr 时候会上锁，大部分情况下都不在锁内，所以可以保证效率

#include <thread>
#include <chrono>
#include <memory>
#include <iostream>
#include <map>
#include <mutex>

#if !defined(_WIN32)
#include <sys/stat.h>
#endif

bool is_file(const char* path) {
	struct stat statbuf;
	if (stat(path, &statbuf) == 0) {
		if ((statbuf.st_mode & S_IFMT) == S_IFREG) {
			return true;
		}
	}
	return false;
}

class FileWapper
{
public:
	using Ptr = std::shared_ptr<FileWapper>;
	FileWapper(FILE* oFile) {
		m_fp = oFile;
	}
	~FileWapper() {
		if (m_fp)
		{
			fclose(m_fp);
			m_fp = NULL;
		}
	}
	FILE* m_fp = NULL;
};
class WriteFile
{
public:

	~WriteFile()
	{
		m_map_file.clear();
	}
	static WriteFile& GetInstans()
	{
		static WriteFile g_WriteFile;
		return g_WriteFile;
	}
	// isReplace 是否替换已有文件 true 替换已有文件 false 不替换已有文件直接返回错误
	int W2File(char* file_path, char* data, int len, bool isReplace = true)
	{
		if (file_path == NULL)
		{
			return -1;
		}
		std::string t_path = file_path;
		FileWapper::Ptr file_ptr = GetFilePtr(t_path);
		int ret = 0;
		if (file_ptr)
		{

			ret = fwrite(data, 1, len, file_ptr->m_fp);
		}
		else
		{
			// 首次创建文件，如果发现文件已经存在，是否替换
			/********Start *********/
			if (!isReplace)
			{
				bool isFile = is_file(t_path.c_str());
				if (isFile)
				{
					printf("file is exit t_path:%s \n", t_path.c_str());
					return -2;
				}
			}
		
			/********End  *********/

			FILE* fp = fopen(t_path.c_str(), "wb");
			if (NULL == fp)
			{
				return -1;
			}

			FileWapper::Ptr ptr(new FileWapper(fp));
			{
				std::lock_guard<std::mutex> lk(mux_file);

				m_map_file[t_path] = ptr;
			}

			ret = fwrite(data, 1, len, ptr->m_fp);
		}
		return ret;
	}
	int CloseFile(char* file_path)
	{
		if (file_path == NULL)
		{
			return -1;
		}
		std::string t_path = file_path;

		FileWapper::Ptr file_ptr = GetFilePtr(t_path, true);
		if (file_ptr)
		{
			while (1)
			{
				if (file_ptr.use_count() == 1)
				{
					file_ptr.reset();
					break;
				}
				std::this_thread::sleep_for(std::chrono::milliseconds(1));
			}
		}

		return 0;
	}
private:
	WriteFile() {}

	FileWapper::Ptr GetFilePtr(std::string key, bool isErase = false)
	{
		FileWapper::Ptr ptr;
		std::lock_guard<std::mutex> lk(mux_file);
		std::map<std::string, FileWapper::Ptr>::iterator it = m_map_file.find(key);
		if (it != m_map_file.end())
		{
			ptr = it->second;
			if (isErase)
			{
				m_map_file.erase(it);
			}
		}
		return ptr;
	}

private:

	std::map<std::string, FileWapper::Ptr>  m_map_file;
	std::mutex mux_file;
};



class ReadFile
{
public:

	~ReadFile()
	{
		m_map_file.clear();
	}
	static ReadFile& GetInstans()
	{
		static ReadFile g_ReadFile;
		return g_ReadFile;
	}
	// 
	int RFile(char* file_path, char* data, int len)
	{
		if (file_path == NULL)
		{
			return -1;
		}
		std::string t_path = file_path;
		FileWapper::Ptr file_ptr = GetFilePtr(t_path);

		int ret = 0;
		if (file_ptr)
		{
	
			ret = fread(data, 1, len, file_ptr->m_fp);
		}
		else
		{
		
			FILE* fp = fopen(t_path.c_str(), "rb");
			if (NULL == fp)
			{
				return -1;
			}

			FileWapper::Ptr ptr(new FileWapper(fp));
			{
				std::lock_guard<std::mutex> lk(mux_file);

				m_map_file[t_path] = ptr;
			}

			ret = fread(data, 1, len, ptr->m_fp);
		}
		return ret;
	}
	int CloseFile(char* file_path)
	{
		if (file_path == NULL)
		{
			return -1;
		}
		std::string t_path = file_path;

		FileWapper::Ptr file_ptr = GetFilePtr(t_path, true);
		if (file_ptr)
		{
			while (1)
			{
				if (file_ptr.use_count() == 1)
				{
					file_ptr.reset();
					break;
				}
				std::this_thread::sleep_for(std::chrono::milliseconds(1));
			}
		}

		return 0;
	}
private:
	ReadFile() {}

	FileWapper::Ptr GetFilePtr(std::string key, bool isErase = false)
	{
		FileWapper::Ptr ptr;
		std::lock_guard<std::mutex> lk(mux_file);
		std::map<std::string, FileWapper::Ptr>::iterator it = m_map_file.find(key);
		if (it != m_map_file.end())
		{
			ptr = it->second;
			if (isErase)
			{
				m_map_file.erase(it);
			}
		}
		return ptr;
	}
private:
	std::mutex mux_file;
	std::map<std::string, FileWapper::Ptr>  m_map_file;
};

// 这里用宏重定义一下，可以直接用宏定义调用，也可以裸着调用，具体见demo
// NAME char *
// DATA char *
// LEN  int
#define  DUMPCPP(NAME,DATA,LEN)  {\
                        char temp[128] = {0};      \
						sprintf(temp, "%p_%s",this, (NAME));  \
						WriteFile::GetInstans().W2File((char *)temp, (char*)(DATA), (LEN)); \
                    }

#define  DUMP_DATA(NAME,DATA,LEN,ISRE) WriteFile::GetInstans().W2File((char *)NAME, (char*)(DATA), (LEN), (ISRE));
#define  DUMP_CLOSE(NAME)  WriteFile::GetInstans().CloseFile((char *)NAME);


#define  READ_DATA(NAME,DATA,LEN)  ReadFile::GetInstans().W2File((char *)NAME, (char*)(DATA), (LEN));
#define  READ_CLOSE(NAME) ReadFile::GetInstans().CloseFile((char *)NAME);


